package com.lap.auth.application.menu.assembler;

import com.lap.auth.application.menu.command.CreateMenuCmd;
import com.lap.auth.application.menu.command.UpdateMenuCmd;
import com.lap.auth.application.menu.dto.MenuNodeDto;
import com.lap.auth.domain.menu.Menu;
import com.lap.auth.domain.menu.MenuType;
import com.lap.auth.domain.shared.tools.BaseConverter;
import com.lap.framework.tools.BooleanStrategy;
import com.lap.framework.tools.EnumUtil;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface MenuAssembler extends BaseConverter {
  Integer ZERO = 0;

  @Mapping(target = "id", ignore = true)
  @Mapping(target = "pid", source = "pidPaths")
  @Mapping(target = "pidPath", source = "pidPaths")
  @Mapping(target = "meta.hide", source = "meta.isHide")
  @Mapping(target = "meta.keepAlive", source = "meta.isKeepAlive")
  @Mapping(target = "meta.affix", source = "meta.isAffix")
  @Mapping(target = "meta.iframe", source = "meta.isIframe")
  Menu toDomain(CreateMenuCmd cmd);

  @Mapping(target = "id", source = "id")
  @Mapping(target = "pid", source = "cmd.pidPaths")
  @Mapping(target = "pidPath", source = "cmd.pidPaths")
  @Mapping(target = "type", source = "cmd.type")
  @Mapping(target = "meta.hide", source = "cmd.meta.isHide")
  @Mapping(target = "meta.keepAlive", source = "cmd.meta.isKeepAlive")
  @Mapping(target = "meta.affix", source = "cmd.meta.isAffix")
  @Mapping(target = "meta.iframe", source = "cmd.meta.isIframe")
  Menu toDomain(Integer id, UpdateMenuCmd cmd);

  @Mapping(target = "pidPaths", source = "pidPath")
  @Mapping(target = "meta.isHide", source = "meta.hide")
  @Mapping(target = "meta.isKeepAlive", source = "meta.keepAlive")
  @Mapping(target = "meta.isAffix", source = "meta.affix")
  @Mapping(target = "meta.isIframe", source = "meta.iframe")
  @Mapping(target = "children", ignore = true)
  MenuNodeDto toDto(Menu menu);

  List<MenuNodeDto> toDtoList(List<Menu> list);

  default Integer boolToInt(boolean value) {
    return BooleanStrategy.toInteger(value);
  }

  default boolean intToBool(Integer value) {
    return BooleanStrategy.toBoolean(value);
  }

  default MenuType codeToMenuType(Integer code) {
    return EnumUtil.fromCode(MenuType.class, code);
  }

  default Integer menuTypeToCode(MenuType type) {
    return type.getCode();
  }

  default List<MenuNodeDto> convertToTree(List<MenuNodeDto> allList) {
    List<MenuNodeDto> roots = new ArrayList<>();
    Map<Integer, MenuNodeDto> nodeMap = new HashMap<>();
    for (MenuNodeDto dto : allList) {
      dto.setChildren(new ArrayList<>());
      nodeMap.put(dto.getId(), dto);
    }

    for (MenuNodeDto dto : allList) {
      if (Objects.equals(dto.getPid(), ZERO)) {
        roots.add(dto);
      } else {
        MenuNodeDto parent = nodeMap.get(dto.getPid());
        if (parent != null) {
          parent.getChildren().add(dto);
        }
      }
    }

    for (MenuNodeDto dto : nodeMap.values()) {
      dto.getChildren().sort(Comparator.comparing(MenuNodeDto::getSortOrder));
    }
    roots.sort(Comparator.comparing(MenuNodeDto::getSortOrder));
    return roots;
  }
}
